/*-------------------------------------------------------------------------
 - Copyright (c) 2024-2025 [XD-AMCC TEAM]
 - [XD-AMCC] is licensed under Mulan PSL v2.
 - You can use this software according to the terms and conditions of the Mulan PSL v2.
 - You may obtain a copy of Mulan PSL v2 at:
 -          http://license.coscl.org.cn/MulanPSL2
 - THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES
 - OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
 - TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 - See the Mulan PSL v2 for more details.
 ------------------------------------------------------------------------*/

/**
 * @brief  Semantic actions for translating statements.
 *
 * @author  WXQ#XDU
 * @date    2025.01
 */

#include <assert.h>

#include "_emit_inner.h"



// Prepare attribute object for stmt:
//   record the first code id of stmt,
//   create an empty .nc
//
static void  emit_prepare_stmt__(t_emitter_ptr pEmitter, t_tree_ptr stmtNode) {
    t_codeattr_ptr myAttr = codeattr_new();
    myAttr->firstCodeId = codemgr_nextstat(pEmitter->ircodemgrPtr);
    tree_set_annotation(stmtNode, myAttr);
}


// pre-semantic-action for statement:
//   prepare attribute object for all kinds of child node.
//
// This is the common work of all kinds of statement.
//
void emit_enter_statement(t_tree_listener_ptr listenerPtr, t_tree_ptr node) {
    t_emitter_ptr pEmitter  = (t_emitter_ptr)listenerPtr;
    t_tree_ptr    childNode = tree_get_child(node, 0);
    emit_prepare_stmt__(pEmitter, childNode);
}


// post-semantic-action for statement
//   pass attribute in bottom-up
//
void emit_exit_statement(t_tree_listener_ptr listenerPtr, t_tree_ptr node) {
    emit_passAttributeOfChild__(node, 0);
}



// post-semantic-action for selectionStatement
//   perform actions for productions:
//   S -> if "(" E ")" S1
//      | if "(" E ")" S1 else S2
//
void emit_exit_selectionStmt(t_tree_listener_ptr listenerPtr, t_tree_ptr node) {
    t_tree_ptr  exprNode  = tree_get_child(node, 2); // conditional expr
    t_tree_ptr  elseNode  = tree_get_child(node, 5); // "ELSE" node, may be null
    t_tree_ptr  stmtNode1 = tree_get_child(node, 4); // stmt of true-branch

    // created in emit_enter_statement()
    t_codeattr_ptr myAttr    = (t_codeattr_ptr)tree_get_annotation(node);
    t_codeattr_ptr exprAttr  = (t_codeattr_ptr)tree_get_annotation(exprNode);
    t_codeattr_ptr stmtAttr1 = (t_codeattr_ptr)tree_get_annotation(stmtNode1);

    // backpatch( E.tc, M.stat)
    emitter_backpatch( exprAttr->trueChain, stmtAttr1->firstCodeId,
                       "E" CHAIN_TC );

    if ( NULL == elseNode ) {  // production: S -> IF ( E ) S1
        // S.nc = merge( E.fc, S1.nc )
        myAttr->nextChain =
        emitter_mergeChain( exprAttr->falseChain, "E"  CHAIN_FC,
                            stmtAttr1->nextChain, "S1" CHAIN_NC,
                            myAttr->nextChain   , "S"  CHAIN_NC);
    }
    else {      // production: S -> IF (E) S1 ELSE S2
        t_tree_ptr     stmtNode2 = tree_get_child(node, 6); // false-branch
        t_codeattr_ptr stmtAttr2 = (t_codeattr_ptr)tree_get_annotation(stmtNode2);
        t_codeattr_ptr elseAttr  = (t_codeattr_ptr)tree_get_annotation(elseNode);

        // backpatch( E.fc, M2.stat)
        emitter_backpatch(  exprAttr->falseChain, stmtAttr2->firstCodeId,
                            "E" CHAIN_FC );

        // S.nc = merge( S1.nc, S2.nc, N.nc )
        //      N.nc(ELSE.nc) was created in  emit_visit_terminal("ELSE")
        myAttr->nextChain =
        emitter_mergeChain(stmtAttr1->nextChain,  "S1" CHAIN_NC,
                           stmtAttr2->nextChain,  "S2" CHAIN_NC,
                           myAttr->nextChain, "S"  CHAIN_NC);
        myAttr->nextChain =
        emitter_mergeChain(elseAttr->nextChain, CODEDESC_ELSE CHAIN_NC,
                           NULL, NULL,
                           myAttr->nextChain, "S"  CHAIN_NC);
    }
}



// pre-semantic-action for labeledStatement:
//   record the 'nextstat', next code id defined by current label name,
//   and perform back-patch on daemon.
//
void emit_enter_labeledStmt(t_tree_listener_ptr listenerPtr, t_tree_ptr node) {
    t_emitter_ptr pEmitter   = (t_emitter_ptr)listenerPtr;

    // This production: labeledStatement -> ID : statement
    t_tree_ptr    labelNode  = tree_get_child  ( node, 0 );
    t_ircode_id   targetCode = codemgr_nextstat( pEmitter->ircodemgrPtr );

    emitter_setTargetOfLabel( pEmitter, labelNode, targetCode );
}


// post-semantic-action for labeledStatement:
//
void emit_exit_labeledStmt(t_tree_listener_ptr listenerPtr, t_tree_ptr node) {
    t_codeattr_ptr myAttr   = (t_codeattr_ptr) tree_get_annotation(node);
    t_tree_ptr     stmtNode = tree_get_child(node, 2);
    t_codeattr_ptr stmtAttr = (t_codeattr_ptr) tree_get_annotation(stmtNode);

    // For S -> ID : S1,  S.nc = S1.nc
    if ( NULL != stmtAttr ) {
        ASSIGN_CHAIN( myAttr->nextChain  , "S"  CHAIN_NC,
                      stmtAttr->nextChain, "label-S1" CHAIN_NC);
    }
}


// pre-semantic-action for compoundStatement:
//   prepare attribute object for function body.
//
void emit_enter_compoundStmt(t_tree_listener_ptr listenerPtr, t_tree_ptr node) {
    t_codeattr_ptr myAttr = (t_codeattr_ptr) tree_get_annotation(node);
    if ( NULL != myAttr ) return;

    // Function body in funDeclaration is a compoundStatement,
    // its attribute object is created at here.
    t_emitter_ptr pEmitter = (t_emitter_ptr)listenerPtr;
    emit_prepare_stmt__(pEmitter, node);
}


// post-semantic-action for compoundStatement:
//   just pass attribute in bottom-up
//
void emit_exit_compoundStmt(t_tree_listener_ptr listenerPtr, t_tree_ptr node) {
     // created by emit_entry_stmt()
    t_codeattr_ptr myAttr  = (t_codeattr_ptr) tree_get_annotation(node);
    t_emitter_ptr pEmitter = (t_emitter_ptr) listenerPtr;

    // If compound statement has no codes,
    // emit a noop instruction for visual completeness
    t_ircode_id nextCodeId = codemgr_nextstat(pEmitter->ircodemgrPtr);
    if ( nextCodeId == myAttr->firstCodeId ) {
        codemgr_emit(pEmitter->ircodemgrPtr, OP_NOOP,
                NULL, NULL, NULL);
        return;
    }

    // At here , we use the actions in dragon book, section 6.7.3
    // S -> "{" L "}"   { S.nc = L.nc }
    // S -> A           { S.nc = null }
    // L -> S           { L.nc = S.nc }
    // L -> L1 M S      { backpatch(L1.nc,  M.stat);   L.nc = S.nc }
    // M -> Epsilon     { M.stat = nextstat }
    //
    // But, in the grammar of AMC, L is defined in EBNF style:
    // L -> ( S | D )*

    // See doc/emitter-misc.md for more detail.

    t_list_iterator iter;
    LIST_ITERATOR_INIT( &iter, tree_get_children(node) );
    t_list_node_ptr  listNode = LIST_ITERATOR_BEGIN( &iter ); // "{"
    t_list_node_ptr  endNode  = LIST_ITERATOR_END  ( &iter );
    listNode = LIST_ITERATOR_NEXT( &iter );

    t_tree_ptr     nextStmtNode = (t_tree_ptr)list_node_get_value(listNode);
    t_codeattr_ptr lastStmtAttr = (t_codeattr_ptr) tree_get_annotation(nextStmtNode);
    t_codeattr_ptr nextStmtAttr = NULL;
    listNode = LIST_ITERATOR_NEXT( &iter );
    for ( ;  listNode != endNode; listNode = LIST_ITERATOR_NEXT( &iter ) ) {
        nextStmtNode = (t_tree_ptr)list_node_get_value(listNode);
        nextStmtAttr = (t_codeattr_ptr) tree_get_annotation(nextStmtNode);
        if ( NULL == nextStmtAttr )
            continue;

        // for a sequence of statements, such as S1 S2 S3,
        // S1.next = S2.firstCode, S2.next = S3.firstCode
        emitter_backpatch( lastStmtAttr->nextChain,
                           nextStmtAttr->firstCodeId,
                           "prevStmt" CHAIN_NC );
        lastStmtAttr = nextStmtAttr;
    }

    // For S -> "{" L "}",   S.nc = L.nc
    if ( NULL != lastStmtAttr ) {
        ASSIGN_CHAIN( myAttr->nextChain      ,  "S" CHAIN_NC,
                      lastStmtAttr->nextChain,  "L" CHAIN_NC );
    }
}


// pre-semantic-action for blockItem:
//   prepare attribute object for varDeclaration.
//
void emit_enter_blockItemStmt(t_tree_listener_ptr listenerPtr, t_tree_ptr node) {
    t_emitter_ptr  pEmitter  = (t_emitter_ptr)listenerPtr;
    t_tree_ptr     childNode = tree_get_child(node, 0);

    // For a statement, its attribute object will
    // be created by emit_enter_stmt()
    if ( tree_is_markedByNT(childNode, NT_statement) )
        return;

    // For a variable declaration, create its attribute object
    // that records the first code for variable initialization, maybe.
    emit_prepare_stmt__(pEmitter, childNode);
}


// post-semantic-action for blockItem:
//   just pass attribute in bottom-up
//
void emit_exit_blockItemStmt(t_tree_listener_ptr listenerPtr, t_tree_ptr node) {
    emit_passAttributeOfChild__(node, 0);
}



// post-semantic-action for expressionStatement:
//   Perform back-patch on demand, because any boolean-expr is an expression.
//
// For example, the following statements are all valid in C and AMC:
//      10;
//      x < y;
//      x && y || x-y;
// the last two require back-patching their goto instructions
// in true-chain and false-chain.
//
void emit_exit_expressionStmt(t_tree_listener_ptr listenerPtr, t_tree_ptr node) {
    if ( tree_get_childCount(node) == 1)  // no expression, just ";"
    {
        // emit a noop instruction for visual completeness
        codemgr_emit(  ((t_emitter_ptr)listenerPtr)->ircodemgrPtr,
                        OP_NOOP, NULL, NULL, NULL );
        return;
    }

    //  .nc  SHOULD be empty
    t_codeattr_ptr myAttr   = (t_codeattr_ptr) tree_get_annotation(node);
    assert ( list_is_empty( myAttr->nextChain) );

    // perform back-patching  for  boolean-expr

    t_emitter_ptr  pEmitter = (t_emitter_ptr)listenerPtr;
    t_ircode_id    nextCode = codemgr_nextstat(pEmitter->ircodemgrPtr);
    t_tree_ptr     exprNode = tree_get_child(node, 0);
    t_codeattr_ptr exprAttr = (t_codeattr_ptr) tree_get_annotation(exprNode);
    if ( EXPR_IS_BOOL(exprAttr) ) {
        emitter_backpatch(exprAttr->trueChain,  nextCode, "E" CHAIN_TC);
        emitter_backpatch(exprAttr->falseChain, nextCode, "E" CHAIN_FC);
    }
}


// Helper: emit code for a "return" or "return expr" of source program
static void emit_return__(  t_emitter_ptr pEmitter,
                            t_tree_ptr    funcNameNode,
                            t_tree_ptr    jumpTokenNode,
                            t_tree_ptr    exprNode)
{
    t_type_ptr funcType       = codemgr_getDataType(funcNameNode);
    t_type_ptr funcReturnType = type_func_return(funcType);

// CASE 1: for a function returns "void"
    if ( TK_TYPE_VOID == type_var_basekind(funcReturnType) ) {
        // Check: the return-statement has extra expr ?
        if ( NULL != exprNode ) {
            t_position pos = tree_get_startPosition(jumpTokenNode);
            ERROR_REPORT_INPUT(pos.filePath,pos.line, pos.column,
                    EC_EXTRA_RET_VAL, tree_get_markText(funcNameNode) );
        }
        // emit return-instruction without value
        codemgr_emit(pEmitter->ircodemgrPtr, OP_RETURN, NULL,NULL,NULL);
        return ;
    }

// CASE 2: for a function MUST return a value

    t_ir_address_ptr  returnValueAddr = NULL;
    const char * comment = NULL;
    if ( NULL == exprNode ) {  // miss express for return ?
        t_position pos = tree_get_startPosition(jumpTokenNode);
        ERROR_REPORT_INPUT(pos.filePath,pos.line, pos.column,
                    EC_MISSRET_VAL, tree_get_markText(funcNameNode) );
        returnValueAddr = codemgr_newNumber(pEmitter->ircodemgrPtr, RETCODE_MISS_VAL);
        comment = CODEDESC_NORET_VAL;
    } else {
        t_codeattr_ptr exprAttr = (t_codeattr_ptr)tree_get_annotation (exprNode);
        if (NULL != exprAttr) {
            returnValueAddr = emit_getExprAddress__(pEmitter->ircodemgrPtr, exprAttr);
        } else {
            ERROR_REPORT_BUG("null attribute for return-expression in %s", __FUNCTION__);
            returnValueAddr = codemgr_newNumber(pEmitter->ircodemgrPtr, RETCODE_MISS_ATTR);
            comment = CODEDESC_BUG;
        }
    }

    // emit return-instruction with value
    t_ircode_ptr codePtr =  codemgr_emit( pEmitter->ircodemgrPtr,
                    OP_RETURN_VAL, NULL, NULL, returnValueAddr);
    codePtr->comment.text = comment;
}


// Find a tree node which is marked by non-terminal iterationStatement
// and is the nearest ancestor of argument 'node'.
//
static t_tree_ptr   findLoopStmtNode__(t_tree_ptr node) {
    while ( NULL != node ) {
        if ( tree_is_markedByNT(node, NT_iterationStatement) ) {
            return node;
        }
        node = tree_get_parent(node);
    }

    return NULL;
}



// post-semantic-action for jumpStatement:
//     emit unconditional goto, or return instruction.
//
void emit_exit_jumpStmt(t_tree_listener_ptr listenerPtr, t_tree_ptr node) {
    t_emitter_ptr pEmitter = (t_emitter_ptr)listenerPtr;
    t_tree_ptr    jumpTokenNode = tree_get_child(node, 0);
    EnumTokenKind jumpTokenKind = tree_get_tokenKind(jumpTokenNode);

    if ( TK_KW_RETURN == jumpTokenKind ) {
        t_tree_ptr   funcNameNode = tree_get_child(pEmitter->currFuncInfo->funcDeclNode, 1);
        t_tree_ptr   exprNode     = tree_get_child(node, 1);
        if ( false == tree_is_markedByNT(exprNode, NT_expression) )
            exprNode = NULL;
        emit_return__( pEmitter, funcNameNode, jumpTokenNode, exprNode );

        return ;
    }

    if ( TK_KW_GOTO == jumpTokenKind ) {
        t_tree_ptr       labelNameNode = tree_get_child(node, 1);
        t_ircode_id      targetId      = emitter_getTargetOfLabel( pEmitter, labelNameNode );
        t_ir_address_ptr targetAddress = codemgr_newNumber(pEmitter->ircodemgrPtr, targetId);
        t_ircode_ptr
        codePtr = codemgr_emit( pEmitter->ircodemgrPtr,  OP_GOTO,
                                NULL, NULL, targetAddress );
        if ( 0 == targetId ) {
            // meet a forward reference to a label
            emitter_makeChainOfLabel( pEmitter, labelNameNode, codePtr );
            codePtr->comment.text = CODEDESC_BACKPATCHED;
        }

        return ;
    }

    if ( TK_KW_BREAK == jumpTokenKind ) {
        t_tree_ptr loopStmtNode =  findLoopStmtNode__(node);
        if ( NULL == loopStmtNode ) {
            t_position pos = tree_get_startPosition(jumpTokenNode);
            ERROR_REPORT_INPUT(pos.filePath, pos.line, pos.column, EC_NOTINLOOP, "break");
            return;
        }

        // emit "goto -" for S -> break;
        // record this incomplete goto in iterationStatement.nc,
        t_ir_address_ptr targetAddress = codemgr_unknownJumpAddress(pEmitter->ircodemgrPtr);
        t_ircode_ptr gotoCode = codemgr_emit(pEmitter->ircodemgrPtr,
                         OP_GOTO,   NULL,   NULL,  targetAddress);
        gotoCode->comment.text = CODEDESC_BREAK ", " CODEDESC_BACKPATCHED;

        t_codeattr_ptr loopAttr = (t_codeattr_ptr)tree_get_annotation(loopStmtNode);
        // loopAttr->nextChain = emitter_add2Chain(loopAttr->nextChain,
        //                     gotoCode, "while" CHAIN_NC);
        t_list_ptr break_NC = emitter_makeChain(gotoCode, CODEDESC_BREAK  CHAIN_NC);
        loopAttr->nextChain = emitter_mergeChain(
                    break_NC, CODEDESC_BREAK  CHAIN_NC,
                    NULL,   NULL,
                    loopAttr->nextChain,  "while" CHAIN_NC );
        return ;
    }

    if ( TK_KW_CONTINUE == jumpTokenKind ) {
        t_tree_ptr loopStmtNode =  findLoopStmtNode__(node);
        if ( NULL == loopStmtNode ) {
            t_position pos = tree_get_startPosition(jumpTokenNode);
            ERROR_REPORT_INPUT(pos.filePath, pos.line, pos.column, EC_NOTINLOOP, "continue");
            return;
        }

        // emit :  goto <beginning-of-loop>
        t_codeattr_ptr   loopAttr  = (t_codeattr_ptr)tree_get_annotation(loopStmtNode);
        t_ir_address_ptr loopBegin = codemgr_newNumber(pEmitter->ircodemgrPtr,
                                        loopAttr->firstCodeId);
        t_ircode_ptr gotoCode = codemgr_emit(pEmitter->ircodemgrPtr, OP_GOTO,
                                NULL, NULL, loopBegin);
        gotoCode->comment.text = CODEDESC_CONTINUE;

        return;
    }

}


// post-semantic-action for iterationStatement,
//  S -> while ( E ) S1
//
void emit_exit_iterationStmt(t_tree_listener_ptr listenerPtr, t_tree_ptr node) {
    t_emitter_ptr  pEmitter = (t_emitter_ptr)listenerPtr;
    t_codeattr_ptr loopAttr = (t_codeattr_ptr)tree_get_annotation(node);
    t_tree_ptr     exprNode = tree_get_child(node, 2);
    t_codeattr_ptr exprAttr = (t_codeattr_ptr)tree_get_annotation(exprNode);
    t_tree_ptr     stmtNode = tree_get_child(node, 4);
    t_codeattr_ptr stmtAttr = (t_codeattr_ptr)tree_get_annotation(stmtNode);

    // backpatch( S1.nc, M1.stat ), M1.stat == E.begin
    emitter_backpatch(  stmtAttr->nextChain, loopAttr->firstCodeId,
                        "loopBody" CHAIN_NC );

    // backpatch( E.tc, M2.stat ),  M2.stat == S1.begin
    emitter_backpatch(  exprAttr->trueChain, stmtAttr->firstCodeId,
                        "loopExpr" CHAIN_TC );

    // NOTICE:   S.nc = E.fc  is the original action,
    // BUT we support "break" loop, so action should be :
    //           S.nc = merge( E.fc, S.nc )
    loopAttr->nextChain = emitter_mergeChain(
                exprAttr->falseChain, "loopExpr" CHAIN_FC,
                NULL,   NULL,
                loopAttr->nextChain,  "while" CHAIN_NC );

    // Finally, we should emit a jump instruction to loop-begin
    t_ir_address_ptr loopBegin = codemgr_newNumber(pEmitter->ircodemgrPtr,
                                    loopAttr->firstCodeId);
    t_ircode_ptr gotoCode =  codemgr_emit(pEmitter->ircodemgrPtr, OP_GOTO,
                            NULL, NULL, loopBegin);
    gotoCode->comment.text = CODEDESC_LOOPBODY_END;
}